package com.tao.lab.bitmaprecycle;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ProjectName: lab
 * @Package: com.tao.lab.bitmaprecycle
 * @ClassName: GroupedLinkedMap
 * @Description: java类作用描述
 * @Author: wangxf
 * @CreateDate: 2022/5/26 9:20
 * @UpdateUser: 更新者
 * @UpdateDate: 2022/5/26 9:20
 * @UpdateRemark: 更新说明
 * @Version: 1.0
 */
class GroupedLinkedMap<K extends Poolable, V> {
    private final GroupedLinkedMap.LinkedEntry<K, V> head = new GroupedLinkedMap.LinkedEntry();
    private final Map<K, LinkedEntry<K, V>> keyToEntry = new HashMap();

    GroupedLinkedMap() {
    }

    public void put(K key, V value) {
        GroupedLinkedMap.LinkedEntry<K, V> entry = (GroupedLinkedMap.LinkedEntry)this.keyToEntry.get(key);
        if (entry == null) {
            entry = new GroupedLinkedMap.LinkedEntry(key);
            this.makeTail(entry);
            this.keyToEntry.put(key, entry);
        } else {
            key.offer();
        }

        entry.add(value);
    }

    public V get(K key) {
        GroupedLinkedMap.LinkedEntry<K, V> entry = (GroupedLinkedMap.LinkedEntry)this.keyToEntry.get(key);
        if (entry == null) {
            entry = new GroupedLinkedMap.LinkedEntry(key);
            this.keyToEntry.put(key, entry);
        } else {
            key.offer();
        }

        this.makeHead(entry);
        return entry.removeLast();
    }

    public V removeLast() {
        for(GroupedLinkedMap.LinkedEntry last = this.head.prev; !last.equals(this.head); last = last.prev) {
            V removed = (V) last.removeLast();
            if (removed != null) {
                return removed;
            }

            removeEntry(last);
            this.keyToEntry.remove(last.key);
            ((Poolable)last.key).offer();
        }

        return null;
    }

    public String toString() {
        StringBuilder sb = new StringBuilder("GroupedLinkedMap( ");
        GroupedLinkedMap.LinkedEntry<K, V> current = this.head.next;

        boolean hadAtLeastOneItem;
        for(hadAtLeastOneItem = false; !current.equals(this.head); current = current.next) {
            hadAtLeastOneItem = true;
            sb.append('{').append(current.key).append(':').append(current.size()).append("}, ");
        }

        if (hadAtLeastOneItem) {
            sb.delete(sb.length() - 2, sb.length());
        }

        return sb.append(" )").toString();
    }

    private void makeHead(GroupedLinkedMap.LinkedEntry<K, V> entry) {
        removeEntry(entry);
        entry.prev = this.head;
        entry.next = this.head.next;
        updateEntry(entry);
    }

    private void makeTail(GroupedLinkedMap.LinkedEntry<K, V> entry) {
        removeEntry(entry);
        entry.prev = this.head.prev;
        entry.next = this.head;
        updateEntry(entry);
    }

    private static <K, V> void updateEntry(GroupedLinkedMap.LinkedEntry<K, V> entry) {
        entry.next.prev = entry;
        entry.prev.next = entry;
    }

    private static <K, V> void removeEntry(GroupedLinkedMap.LinkedEntry<K, V> entry) {
        entry.prev.next = entry.next;
        entry.next.prev = entry.prev;
    }

    private static class LinkedEntry<K, V> {
        private final K key;
        private List<V> values;
        GroupedLinkedMap.LinkedEntry<K, V> next;
        GroupedLinkedMap.LinkedEntry<K, V> prev;

        public LinkedEntry() {
            this((K) null);
        }

        public LinkedEntry(K key) {
            this.next = this.prev = this;
            this.key = key;
        }

        public V removeLast() {
            int valueSize = this.size();
            return valueSize > 0 ? this.values.remove(valueSize - 1) : null;
        }

        public int size() {
            return this.values != null ? this.values.size() : 0;
        }

        public void add(V value) {
            if (this.values == null) {
                this.values = new ArrayList();
            }

            this.values.add(value);
        }
    }
}
